/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <errno.h>
#include <dlfcn.h>
#include <hardware/hardware.h>
#include <hardware/sensors.h>

#define LOG_TAG "legacy_sensors"
#include <cutils/log.h>

#include "legacy_sensors.h"
#include "nusensors.h"

/** Base path of the hal modules */
#define LIBRARY_PATH "/system/lib/hw"

/*****************************************************************************/
/**
 * Load the file defined by the variant and if successful
 * return the dlopen handle and the hmi.
 * @return 0 = success, !0 = failure.
 */
static int load_module(const struct hw_module_t **pHmi)
{
    int status;
    char path[PATH_MAX];
    void *handle = NULL;
    struct hw_module_t *hmi = NULL;
    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
    const char *id = LEGACY_SENSORS_HARDWARE_MODULE_ID;

    /* specify the library path */
    snprintf(path, sizeof(path), "%s/%s.legacy.so", 
             LIBRARY_PATH, id);
    if (access(path, R_OK) != 0) {
        LOGE("load: access %s faild", path);
        status = -EINVAL;
        goto done;
    }

    /*
     * load the symbols resolving undefined symbols before
     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
     * RTLD_NOW the external symbols will not be global
     */
    handle = dlopen(path, RTLD_NOW);
    if (handle == NULL) {
        char const *err_str = dlerror();
        LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
        status = -EINVAL;
        goto done;
    }

    /* Get the address of the struct hal_module_info. */
    hmi = (struct hw_module_t *)dlsym(handle, sym);
    if (hmi == NULL) {
        LOGE("load: couldn't find symbol %s", sym);
        status = -EINVAL;
        goto done;
    }

    /* Check that the id matches */
    if (strcmp(id, hmi->id) != 0) {
        LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
        status = -EINVAL;
        goto done;
    }

    hmi->dso = handle;

    /* success */
    status = 0;

    done:
    if (status != 0) {
        hmi = NULL;
        if (handle != NULL) {
            dlclose(handle);
            handle = NULL;
        }
    } else {
        LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
                id, path, *pHmi, handle);
    }

    *pHmi = hmi;

    return status;
}

/*****************************************************************************/

struct sensors_poll_context_t {
    struct sensors_poll_device_t device; // must be first

        sensors_poll_context_t();
        ~sensors_poll_context_t();
    int activate(int handle, int enabled);
    int setDelay(int handle, int64_t ns);
    int pollEvents(sensors_event_t* event, int count);

private:
    int mCount;
    const struct legacy_sensor_t* mList;
    native_handle_t* mHandle;
    legacy_sensors_module_t *mModule;
    legacy_sensors_control_device_t *mControlDevice;
    legacy_sensors_data_device_t *mDataDevice;
};

/*****************************************************************************/

sensors_poll_context_t::sensors_poll_context_t()
{
    if (load_module((const hw_module_t**)&mModule) == 0) {
        mCount = mModule->get_sensors_list(mModule, &mList);
        if (legacy_sensors_control_open(&mModule->common, &mControlDevice) != 0) {
            LOGE("open sensors control device faild");
        }
        if (legacy_sensors_data_open(&mModule->common, &mDataDevice) != 0) {
            LOGE("open sensors data device faild");
        }
    } else {
        LOGE("open sensors module faild");
    }

    mHandle = mControlDevice->open_data_source(mControlDevice);
    if (mHandle == NULL) {
        LOGE("open data source faild");
    }
    if (mDataDevice->data_open(mDataDevice, mHandle)) {
        LOGE("open data faild");
    }

    //for (int i = 0; i < mCount; i++) {
    //    LOGI("%s %s", mList[i].name, mList[i].vendor);
    //    LOGI("%i %i %i", mList[i].version, mList[i].handle, mList[i].type);
    //    LOGI("%f %f %f", mList[i].maxRange, mList[i].resolution, mList[i].power);
    //}
}

sensors_poll_context_t::~sensors_poll_context_t() {
    if (mDataDevice->data_close(mDataDevice)) {
        LOGE("close data faild");
    }
    if (mControlDevice->close_data_source(mControlDevice) != 0) {
        LOGE("close data source faild");
    }
    if (legacy_sensors_control_close(mControlDevice) != 0) {
        LOGE("close sensors control device faild");
    }
    if (legacy_sensors_data_close(mDataDevice) != 0) {
        LOGE("close sensors data device faild");
    }
}

int sensors_poll_context_t::activate(int handle, int enabled) {
    return mControlDevice->activate(mControlDevice, handle, enabled);
}

int sensors_poll_context_t::setDelay(int handle, int64_t ns) {
    return mControlDevice->set_delay(mControlDevice, 1000 * ns);
}

int sensors_poll_context_t::pollEvents(sensors_event_t* event, int count)
{
    int handle, ret;
    legacy_sensors_data_t data;

    if (count < 1) {
        LOGE("the size of sensors_event[] is zero");
        return 0;
    }

    handle = mDataDevice->poll(mDataDevice, &data);
    handle &= 0x000000ff;

    if (handle >= mCount) {
        LOGE("mDataDevice->poll() return invalid handle: %i", handle);
        return 0;
    }

    //LOGI("%i %i %f %f %f %i", handle, mList[handle].type, 
    //      data.acceleration.x, data.acceleration.y, data.acceleration.z, data.acceleration.status);

    event->version = sizeof(sensors_event_t);
    //event->sensor = data.sensor;
    //event->type = mList[handle].type;
    event->timestamp = data.time;
    switch (mList[handle].type) {
        case LEGACY_SENSOR_TYPE_ACCELEROMETER:
            event->sensor = ID_A;
            event->type = SENSOR_TYPE_ACCELEROMETER;
            event->acceleration.x = data.acceleration.x;
            event->acceleration.y = data.acceleration.y;
            event->acceleration.z = data.acceleration.z;
            event->acceleration.status = data.acceleration.status;
            ret = 1;
            break;
        case LEGACY_SENSOR_TYPE_LIGHT:
            event->sensor = ID_L;
            event->type = SENSOR_TYPE_LIGHT;
            event->light = data.light;
            ret = 1;
            break;
        case LEGACY_SENSOR_TYPE_PROXIMITY:
            event->sensor = ID_P;
            event->type = SENSOR_TYPE_PROXIMITY;
            event->distance = data.distance;
            ret = 1;
            break;
        default:
            LOGE("unknown event type: %i", mList[handle].type);
            ret = 0;
            break;
    }
    return ret;
}

/*****************************************************************************/

static int poll__close(struct hw_device_t *dev)
{
    sensors_poll_context_t *ctx = (sensors_poll_context_t *)dev;
    if (ctx) {
        delete ctx;
    }
    return 0;
}

static int poll__activate(struct sensors_poll_device_t *dev,
        int handle, int enabled) {
    sensors_poll_context_t *ctx = (sensors_poll_context_t *)dev;
    return ctx->activate(handle, enabled);
}

static int poll__setDelay(struct sensors_poll_device_t *dev,
        int handle, int64_t ns) {
    sensors_poll_context_t *ctx = (sensors_poll_context_t *)dev;
    return ctx->setDelay(handle, ns);
}

static int poll__poll(struct sensors_poll_device_t *dev,
        sensors_event_t* event, int count) {
    sensors_poll_context_t *ctx = (sensors_poll_context_t *)dev;
    return ctx->pollEvents(event, count);
}

/*****************************************************************************/

int init_nusensors(hw_module_t const* module, hw_device_t** device)
{
    int status = -EINVAL;

    sensors_poll_context_t *dev = new sensors_poll_context_t();
    memset(&dev->device, 0, sizeof(sensors_poll_device_t));

    dev->device.common.tag = HARDWARE_DEVICE_TAG;
    dev->device.common.version  = 0;
    dev->device.common.module   = const_cast<hw_module_t*>(module);
    dev->device.common.close    = poll__close;
    dev->device.activate        = poll__activate;
    dev->device.setDelay        = poll__setDelay;
    dev->device.poll            = poll__poll;

    *device = &dev->device.common;
    status = 0;
    return status;
}
